/**
 * 
 */
package wpmp.security.mgr.service;

import java.sql.Timestamp;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.type.StandardBasicTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import wcommons.lang.DateUtils;
import wcommons.lang.NumberUtils;
import wcommons.lang.quickbean.Message;
import wpmp.security.mgr.base.BaseService;
import wpmp.security.mgr.dao.RoleDao;
import wpmp.security.mgr.dao.RoleResourceRelaDao;
import wpmp.security.mgr.entity.Role;
import wpmp.security.mgr.entity.RoleResourceRela;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

/**
 * @author Wayne.Wang<5waynewang@gmail.com>
 * @since 9:09:42 PM Mar 15, 2013
 */
@Component
@Transactional(rollbackFor = Exception.class)
public class RoleService extends BaseService {

	private static final Logger LOG = LoggerFactory.getLogger(RoleService.class);

	// 需要更新的字段
	private static final String[] UPDATE_PROPERTIES = { "name", "memo" };

	private static final String SQL_EXISTS_BY_USER_ID = "EXISTS(SELECT 1 FROM sys_user_role_rela WHERE role_id={alias}.id AND user_id=?)";

	private static final String SQL_EXISTS_PARENT = "EXISTS(SELECT 1 FROM sys_role WHERE parent_id={alias}.id AND id=?)";

	private RoleDao dao;
	private RoleResourceRelaDao roleResourceRelaDao;

	@Autowired
	public void setDao(RoleDao dao) {
		this.dao = dao;
	}

	@Autowired
	public void setRoleResourceRelaDao(RoleResourceRelaDao roleResourceRelaDao) {
		this.roleResourceRelaDao = roleResourceRelaDao;
	}

	public Role get(Long id) {
		Role entity = dao.get(id);
		if (entity == null) {
			return entity;
		}
		Role result = new Role();
		BeanUtils.copyProperties(entity, result);
		return result;
	}

	public Role getParent(Long id) {
		return dao.queryForObject(Restrictions.sqlRestriction(SQL_EXISTS_PARENT, id, StandardBasicTypes.LONG));
	}

	public List<Role> getChildren(Long id) {
		Criteria criteria = dao.createCriteria(Restrictions.eq("parentId", id));
		criteria.addOrder(Order.asc("orderNum"));
		return dao.queryForList(criteria);
	}

	/**
	 * 获取子类最大的排序号
	 * @param id
	 * @return
	 */
	int getMaxChildOrderNum(Long id) {
		Integer maxOrderNum = (Integer) dao.createCriteria(Restrictions.eq("parentId", id))
				.setProjection(Projections.max("orderNum")).uniqueResult();
		return maxOrderNum == null ? 0 : maxOrderNum.intValue();
	}

	Message validateName(Role entity) {
		Criteria criteria = dao.createCriteria(Restrictions.eq("parentId", entity.getParentId()),
				Restrictions.eq("name", entity.getName()));
		if (NumberUtils.gtZero(entity.getId())) {
			criteria.add(Restrictions.ne("id", entity.getId()));
		}
		if (dao.queryForInt(criteria) > 0) {
			return Message.getErrorMessage("同级别下已存在角色名称[" + entity.getName() + "]");
		}
		return Message.getDefaultMessage();
	}

	public Message insert(Role entity) {
		final Message message = validateName(entity);
		if (message.notpass()) {
			return message;
		}

		final Timestamp currentTime = DateUtils.newTimestamp();
		entity.setOrderNum(getMaxChildOrderNum(entity.getParentId()) + 1);
		entity.setCreateTime(currentTime);
		entity.setCreateUser(entity.getSysLoginName());
		entity.setUpdateTime(currentTime);
		entity.setUpdateUser(entity.getSysLoginName());
		dao.save(entity);

		final Role parent = dao.get(entity.getParentId());
		entity.setLevel(parent.getLevel() + entity.getId() + ".");
		dao.save(entity);

		message.putAttribute("id", entity.getId());
		message.putAttribute("name", entity.getName());
		message.putAttribute("orderNum", entity.getOrderNum());
		return message;
	}

	public Message update(Role entity) {
		final Message message = validateName(entity);
		if (message.notpass()) {
			return message;
		}

		final Role result = dao.get(entity.getId());

		final String name = result.getName();

		wcommons.beanutils.BeanUtils.copyProperties(result, entity, UPDATE_PROPERTIES, false);

		result.setUpdateTime(DateUtils.newTimestamp());
		result.setUpdateUser(entity.getSysLoginName());
		dao.save(result);

		message.putAttribute("id", entity.getId());
		if (!entity.getName().equals(name)) {
			message.putAttribute("name", entity.getName());
		}
		return message;
	}

	public Message delete(Long id) {
		Role entity = dao.get(id);

		// 重新排序
		dao.decreaseOrderNumWhenGt(entity.getParentId(), entity.getOrderNum());

		List<Role> entities = dao.queryForList(Restrictions.like("level", entity.getLevel(), MatchMode.START));
		Long[] ids = new Long[entities.size()];
		for (int i = 0; i < ids.length; i++) {
			ids[i] = entities.get(i).getId();
		}
		// 删除角色、用户之间的关系
		dao.deleteUserRoleRela(ids);
		// 删除角色、资源、操作之间的关系
		dao.deleteRoleResourceOperationRela(ids);
		// 删除角色、资源之间的关系
		dao.deleteRoleResourceRela(ids);
		// 删除角色
		dao.delete(entities);
		return Message.getDefaultMessage();
	}

	public Message sort(Role entity) {
		Role result = dao.get(entity.getId());
		// 父类没变
		if (NumberUtils.eq(result.getParentId(), entity.getParentId())) {
			// 5 > 1
			if (entity.getOrderNum() > result.getOrderNum()) {
				dao.decreaseOrderNumWhenBetween(entity.getParentId(), result.getOrderNum(), entity.getOrderNum());
			}
			// 1 < 5
			else if (entity.getOrderNum() < result.getOrderNum()) {
				dao.increaseOrderNumWhenBetween(entity.getParentId(), entity.getOrderNum(), result.getOrderNum());
			}
			result.setOrderNum(entity.getOrderNum());
			dao.save(result);
		}
		// 父类变了
		else {
			// 原父类下的排序变动
			dao.decreaseOrderNumWhenGt(result.getParentId(), result.getOrderNum());

			int maxOrderNum = getMaxChildOrderNum(entity.getParentId());

			if (entity.getOrderNum() == null || entity.getOrderNum() > maxOrderNum) {
				entity.setOrderNum(maxOrderNum + 1);
			}
			if (entity.getOrderNum() <= 0) {
				entity.setOrderNum(1);
			}
			if (entity.getOrderNum() <= maxOrderNum) {
				// 递增排序号，腾出空间
				dao.increaseOrderNumWhenGe(entity.getParentId(), entity.getOrderNum());
			}

			final String level = result.getLevel();

			result.setOrderNum(entity.getOrderNum());

			final Role parent = dao.get(entity.getParentId());

			result.setLevel(parent.getLevel() + result.getId() + ".");
			result.setParentId(entity.getParentId());
			dao.save(result);

			// 更新子类的level
			dao.updateChildLevel(level, result.getLevel());
		}

		return Message.getDefaultMessage();
	}

	public Message resetResource(Role entity, String resourceJSONString) throws Exception {
		// 删除角色、资源、操作之前的关系
		dao.deleteRoleResourceOperationRela(entity.getId());
		// 删除角色、资源之前的关系
		dao.deleteRoleResourceRela(entity.getId());

		if (StringUtils.isBlank(resourceJSONString)) {
			return Message.getDefaultMessage();
		}
		JSONArray jsonArray = JSON.parseArray(resourceJSONString);
		if (jsonArray.isEmpty()) {
			return Message.getDefaultMessage();
		}
		Map<RoleResourceRela, Long[]> roleResourceRelas = new HashMap<RoleResourceRela, Long[]>();
		for (int i = 0; i < jsonArray.size(); i++) {
			JSONObject jo = jsonArray.getJSONObject(i);

			RoleResourceRela rrr = new RoleResourceRela();
			rrr.setRoleId(entity.getId());
			rrr.setResourceId(jo.getLong("resourceId"));

			JSONArray ja = jo.getJSONArray("operationIds");
			if (ja == null || ja.isEmpty()) {
				roleResourceRelas.put(rrr, null);
				continue;
			}
			Long[] operationIds = new Long[ja.size()];
			for (int j = 0; j < ja.size(); j++) {
				operationIds[j] = ja.getLongValue(j);
			}
			roleResourceRelas.put(rrr, operationIds);
		}
		// 新增角色、资源之前的关系
		roleResourceRelaDao.save(roleResourceRelas.keySet());

		List<Long> relaIds = new LinkedList<Long>();
		List<Long> operationIds = new LinkedList<Long>();

		for (RoleResourceRela key : roleResourceRelas.keySet()) {
			Long[] value = roleResourceRelas.get(key);
			if (ArrayUtils.isEmpty(value)) {
				continue;
			}
			for (Long operationId : value) {
				relaIds.add(key.getId());
				operationIds.add(operationId);
			}
		}
		// 新增角色、资源、操作之前的关系
		if (CollectionUtils.isNotEmpty(relaIds)) {
			dao.insertRoleResourceOperationRela(relaIds, operationIds);
		}
		return Message.getDefaultMessage();
	}

	/**
	 * 给用户授权角色
	 * @param roleId
	 * @param userIds
	 * @return
	 */
	public Message addUser(Long roleId, Long[] userIds) {
		if (ArrayUtils.isEmpty(userIds)) {
			return Message.getErrorMessage("请选择用户");
		}
		if (!NumberUtils.gtZero(roleId)) {
			return Message.getErrorMessage("请选择角色");
		}
		int[] status = dao.insertUserRoleRela(roleId, userIds);

		if (LOG.isDebugEnabled()) {
			LOG.debug("INSERT UserRoleRela: userIds[{}], roleIds[{}], return [{}]", ArrayUtils.toString(userIds),
					roleId, ArrayUtils.toString(status));
		}

		return Message.getDefaultMessage();
	}

	/**
	 * 取消用户的授权角色
	 * @param roleId
	 * @param userIds
	 * @return
	 */
	public Message removeUser(Long roleId, Long[] userIds) {
		if (ArrayUtils.isEmpty(userIds)) {
			return Message.getErrorMessage("请选择用户");
		}
		if (!NumberUtils.gtZero(roleId)) {
			return Message.getErrorMessage("请选择角色");
		}
		int[] status = dao.deleteUserRoleRela(roleId, userIds);

		if (LOG.isDebugEnabled()) {
			LOG.debug("DELETE UserRoleRela: userIds[{}], roleIds[{}], return [{}]", ArrayUtils.toString(userIds),
					roleId, ArrayUtils.toString(status));
		}

		return Message.getDefaultMessage();
	}

	public List<Role> listByUserId(Long userId) {
		return dao.queryForList(Restrictions.sqlRestriction(SQL_EXISTS_BY_USER_ID, userId, StandardBasicTypes.LONG));
	}

	/**
	 * 判断子类是否叶子节点
	 * @param parentId
	 * @return {id,is_leaf}
	 */
	public SqlRowSet queryChildrenIsLeaf(Long parentId) {
		return dao.queryChildrenIsLeaf(parentId);
	}

	/**
	 * 查询角色授权的资源、操作关系
	 * @param roleId
	 * @return {resource_id,operation_id,level,is_public}
	 */
	public SqlRowSet queryRoleResourceOperationForRowSet(Long roleId) {
		return dao.queryRoleResourceOperationForRowSet(roleId);
	}
}
